import {Layer} from "./Layer";
import {Tile} from "../source/tile/Tile";
import {RasterTileStyle} from "./style/RasterTileStyle";
import {Mesh, MeshBasicMaterial, PlaneBufferGeometry, Texture, TextureLoader} from "three";
import {LRUMap} from "lru_map";
import {FreeMap} from "../";
import {TileSource} from "../source/tile/TileSource";

export class RasterTileLayer extends Layer {

    style: RasterTileStyle;

    lruMap: LRUMap<string, Texture>;

    source: TileSource;

    constructor(layerId: string, sourceId: string) {
        super(layerId, sourceId);

        this.style = new RasterTileStyle();

        this.lruMap = new LRUMap<string, Texture>(50);
    }

    public addTo(map: FreeMap): void {
        super.addTo(map);

        this.source.requestData()
    }

    onSourceDataChange(data: Array<Tile>): void {
        let loader = new TextureLoader();

        this.map.mapRenderer.scene.removeTileZGroup(this.layerId);

        data.forEach(tile => {
            let cacheTile = this.lruMap.get(tile.url);

            if (cacheTile != undefined) {
                this.onLoaded(cacheTile, tile, true);
            } else {
                loader.load(tile.url, texture => this.onLoaded(texture, tile));
            }
        });
    }

    private onLoaded(texture: Texture, tile: Tile, isCache: boolean = false) {
        if (!isCache) {
            this.lruMap.set(tile.url, texture);
        }

        let camera = this.map.mapRenderer.camera;

        let projectCenter = camera.projection(tile.bbox.southWest);

        let tileWidth = tile.bbox.getWidth(camera);

        let tileHeight = tile.bbox.getHeight(camera);

        let material = new MeshBasicMaterial({
            map: texture
        });

        let geometry = new PlaneBufferGeometry(tileWidth, tileHeight);

        let mesh = new Mesh(geometry, material);

        mesh.name = `${this.layerId}_${tile.x}_${tile.y}_${tile.z}`;

        mesh.position.set(projectCenter.x + tileWidth / 2, projectCenter.y + tileHeight / 2, 0);

        this.map.mapRenderer.scene.addTile(this.layerId, mesh, tile.z);
    }
}
